package pcbeonjms;

import java.util.ArrayList;
import javax.jms.*;

/**
 *
 * @author Ady
 */
class ResurseJucator {

    private int LEMN;
    private int PIATRA;
    private int METAL;

    public ResurseJucator(int lemn, int piatra, int metal) {
        this.LEMN = lemn;
        this.PIATRA = piatra;
        this.METAL = metal;
    }

    public int getLemn() {
        return this.LEMN;
    }

    public int getPiatra() {
        return this.PIATRA;
    }

    public int getMetal() {
        return this.METAL;
    }

    public void setLemn(int lemn) {
        this.LEMN = lemn;
    }

    public void setPiatra(int piatra) {
        this.PIATRA = piatra;
    }

    public void setMetal(int metal) {
        this.METAL = metal;
    }

    public boolean existaResurse(ResurseJucator res) {
        return this.LEMN >= res.getLemn() && this.PIATRA >= res.getPiatra() && this.METAL >= res.getMetal();
    }

    public void consumaResurse(ResurseJucator res) {
        this.LEMN = this.LEMN - res.getLemn();
        this.PIATRA = this.PIATRA - res.getPiatra();
        this.METAL = this.METAL - res.getMetal();
    }
}

public class Jucator extends Thread implements MessageListener, ExceptionListener {

    private ResurseJucator resurse;
    private ArrayList<Cladire> cladiri;
    private Cladire target;
    //private Comert comert;
    private int puncte;
    private boolean stop;
    private Main m_joc;
    private final Object m_lockLista = new Object();
    private ArrayList<CladireDeResurse> m_lista;
    private QueueSession m_queueSession;
    private QueueSender m_queueSender;
    private boolean m_poateAnula = false;
    private TopicSession m_topicSession;
    private TopicSession m_tpSesDinComert;
    private TopicConnection m_topicConnection;
    private QueueConnection m_queueConn;

    public Jucator(Main joc, TopicConnectionFactory connFactory,
            Topic topic, QueueConnectionFactory factComert, Queue queComert,
            TopicConnectionFactory dinComertFactory, Topic dinComertTopic,
            String nume) throws JMSException {
        m_joc = joc;
        resurse = new ResurseJucator(10, 5, 2);
        cladiri = new ArrayList<Cladire>(0);
        m_lista = new ArrayList<CladireDeResurse>();
        target = Cladire.daCladire();
        puncte = 0;
        setName(nume);

        m_topicConnection = connFactory.createTopicConnection();
        m_topicSession = m_topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
        TopicSubscriber topicSubscriber = m_topicSession.createSubscriber(topic);
        topicSubscriber.setMessageListener(this);
        m_topicConnection.setExceptionListener(this);
        m_topicConnection.start();

        m_queueConn = factComert.createQueueConnection();
        m_queueSession = m_queueConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
        m_queueSender = m_queueSession.createSender(queComert);
        m_queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

        createListenerForComert(dinComertFactory, dinComertTopic);
    }

    private void createListenerForComert(TopicConnectionFactory dinComertFactory,
            Topic dinComertTopic) throws JMSException {
        TopicConnection topicConnection = dinComertFactory.createTopicConnection();
        m_tpSesDinComert = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
        TopicSubscriber topicSubscriber = m_tpSesDinComert.createSubscriber(dinComertTopic);
        MessageListener listComert = new MessageListener() {

            @Override
            public void onMessage(Message message) {
                TextMessage msg = (TextMessage) message;
                if (msg == null) {
                    System.out.println("null message din comert");
                    return;
                }
                try {
                    String[] split = msg.getText().split(",");
                    if (!split[0].equals(getName())) {
                        return;
                    }
                    Resursa res = Resursa.valueOf(split[1]);
                    int cantitate = Integer.valueOf(split[2]);
                    System.out.println(getName() + " din comert " + cantitate + " " + res);
                    addResurse(res, cantitate);
                } catch (JMSException ex) {
                    ex.printStackTrace(System.out);
                }
            }
        };
        topicSubscriber.setMessageListener(listComert);
        topicConnection.setExceptionListener(this);
        topicConnection.start();
    }

    public void stopPlayer() {
        stop = true;
    }

    @Override
    public void run() {
        try {
            while (target != null && puncte < 1000 && !stop) {
                if (construieste()) {
                    puneOferte();
                } else {
                    if (m_poateAnula) {
                        m_poateAnula = false;
                        anuleazaOferte();
                    }
                    if (construieste()) {
                        puneOferte();
                    } else {
                        //iaResurseProducator();
                    }
                }
            }
            if (!stop) {
                System.err.println("-----Castigator:" + getName() + " si a facut " + puncte);
                m_joc.stopJoc();
            } else {
                System.out.println(getName() + " a facut " + puncte);
            }
            synchronized (m_lockLista) {
                for (CladireDeResurse c : m_lista) {
                    c.interrupt();
                }
            }
            m_lista = null;
        } finally {
            try {
                m_queueConn.close();
            } catch (JMSException ex) {
            }
            try {
                m_topicConnection.close();
            } catch (JMSException ex) {
            }
            try {
                m_queueSender.close();
            } catch (JMSException ex) {
            }
            try {
                m_queueSession.close();
            } catch (JMSException ex) {
            }
            try {
                m_topicSession.close();
            } catch (JMSException ex) {
            }
            try {
                m_tpSesDinComert.close();
            } catch (JMSException ex) {
            }
        }
    }

    private boolean construieste() {

        synchronized (resurse) {
            Main.debug(getName() + "            ---- CONSTRUIESTE ----");
            Main.debug(getName() + "  Resurse existente : " + resurse.getLemn() + " lemn, " + resurse.getPiatra() + " piatra, " + resurse.getMetal() + " metal");
            Main.debug(getName() + "  Construieste cladira " + target.getNumeCladire() + " ( " + target.getResurse().getLemn() + " lemn, " + target.getResurse().getPiatra() + " piatra, " + target.getResurse().getMetal() + " metal )");
            if (resurse.existaResurse(target.getResurse())) {
                this.cladiri.add(target);
                Main.debug(getName() + "  S-a construit cladirea!!");
                System.out.println(getName() + " a construit " + target.getNumeCladire());
                resurse.consumaResurse(target.getResurse());
                puncte = puncte + target.getValoare();
                Main.debug(getName() + "  Resurse ramase: " + resurse.getLemn() + " lemn, " + resurse.getPiatra() + " piatra, " + resurse.getMetal() + " metal");
                cladireConstruita(target);
                target = Cladire.daCladire();
                Main.debug(getName() + "  Cladirea urmatoare: " + target.getNumeCladire() + " ( " + target.getResurse().getLemn() + " lemn, " + target.getResurse().getPiatra() + " piatra, " + target.getResurse().getMetal() + " metal )");
                return true;
            }
            Main.debug(getName() + "  Resurse insuficiente!!");
            return false;
        }
    }

    private void cladireConstruita(Cladire c) {
        if (c.creazaResurse()) {
            CladireDeResurse clad = new CladireDeResurse(c.getResursaConstruita(), c.getCantConstruita(), c.getTimpDeConstruit());
            synchronized (m_lockLista) {
                m_lista.add(clad);
            }
            clad.start();
        }
    }

    private void puneOferte() {
        int dif_Lemn, dif_Piatra, dif_Metal, cantitate = -1;
        Resursa oferita = null, ceruta = null;
        synchronized (resurse) {
            dif_Lemn = resurse.getLemn() - target.getResurse().getLemn();
            dif_Piatra = resurse.getPiatra() - target.getResurse().getPiatra();
            dif_Metal = resurse.getMetal() - target.getResurse().getMetal();
            if (dif_Lemn > 0) {
                oferita = Resursa.LEMN;
                cantitate = dif_Lemn;
                resurse.setLemn(resurse.getLemn() - dif_Lemn);
                if (dif_Piatra < 0) {
                    ceruta = Resursa.PIATRA;
                } else {
                    ceruta = Resursa.METAL;
                }
            } else if (dif_Piatra > 0) {
                oferita = Resursa.PIATRA;
                cantitate = dif_Piatra;
                resurse.setPiatra(resurse.getPiatra() - dif_Piatra);
                if (dif_Lemn < 0) {
                    ceruta = Resursa.LEMN;
                } else {
                    ceruta = Resursa.METAL;
                }
            } else if (dif_Metal > 0) {
                oferita = Resursa.METAL;
                cantitate = dif_Metal;
                resurse.setMetal(resurse.getMetal() - dif_Metal);
                if (dif_Lemn < 0) {
                    ceruta = Resursa.LEMN;
                } else {
                    ceruta = Resursa.PIATRA;
                }
            }
        }
        if (cantitate > 0) {
            try {
                m_poateAnula = true;
                puneOferta(cantitate, oferita, ceruta);
            } catch (JMSException ex) {
                ex.printStackTrace(System.out);
            }
            Main.debug(getName() + "            ---- PUNE OFERTE ----");
            Main.debug(getName() + "  S-a pus oferta : " + cantitate + " " + oferita + " pe " + ceruta);
            Main.debug(getName() + "  Resurse ramase: " + resurse.getLemn() + " lemn, " + resurse.getPiatra() + " piatra, " + resurse.getMetal() + " metal");
        }
    }

    private void puneOferta(int cantitate, Resursa oferita, Resursa ceruta) throws JMSException {
        //comert.puneOferta(this, cantitate, oferita, ceruta);
        TextMessage msg = m_queueSession.createTextMessage();
        msg.setText(getName() + ",pune," + cantitate + "," + oferita + "," + ceruta);
        //System.out.println("Jucator" + msg.getText());
        m_queueSender.send(msg);
    }

    private void anuleazaOferta(Resursa oferita, int cantitate, Resursa primaCeruta, Resursa aDouaCeruta) throws JMSException {
        //comert.anuleazaOferta(this, oferita, -cantitate, primaCeruta, aDouaCeruta);
        TextMessage msg = m_queueSession.createTextMessage();
        msg.setText(getName() + ",anuleaza," + oferita + "," + cantitate + "," + primaCeruta + "," + aDouaCeruta);
        //System.out.println("Jucator" + msg.getText());
        m_queueSender.send(msg);
    }

    private void anuleazaOferte() {
        int dif_Lemn, dif_Piatra, dif_Metal, cantitate, max;
        Resursa oferita, primaCeruta, aDouaCeruta;

        synchronized (this.resurse) {

            dif_Lemn = resurse.getLemn() - target.getResurse().getLemn();
            max = cantitate = dif_Lemn;
            primaCeruta = aDouaCeruta = oferita = Resursa.LEMN;
            dif_Piatra = resurse.getPiatra() - target.getResurse().getPiatra();
            if (dif_Piatra < cantitate) {
                cantitate = dif_Piatra;
                oferita = Resursa.PIATRA;
            }
            if (dif_Piatra > max) {
                max = dif_Piatra;
                primaCeruta = Resursa.PIATRA;
            }
            dif_Metal = resurse.getMetal() - target.getResurse().getMetal();
            if (dif_Metal < cantitate) {
                cantitate = dif_Metal;
                oferita = Resursa.METAL;
            }
            if (dif_Metal > max) {
                max = dif_Metal;
                primaCeruta = Resursa.METAL;
            }
            Main.debug(getName() + "            ---- ANULEAZA OFERTE ----");
            Main.debug(getName() + "  Anuleaza oferta. Resurse necesare: " + dif_Lemn + " lemn, " + dif_Piatra + " piatra, " + dif_Metal + " metal");
            if (oferita == Resursa.LEMN && primaCeruta == Resursa.METAL || oferita == Resursa.METAL && primaCeruta == Resursa.LEMN) {
                aDouaCeruta = Resursa.PIATRA;
            }
            if (oferita == Resursa.LEMN && primaCeruta == Resursa.PIATRA || oferita == Resursa.PIATRA && primaCeruta == Resursa.LEMN) {
                aDouaCeruta = Resursa.METAL;
            }
            if (oferita == Resursa.PIATRA && primaCeruta == Resursa.METAL || oferita == Resursa.METAL && primaCeruta == Resursa.PIATRA) {
                aDouaCeruta = Resursa.LEMN;
            }
            Main.debug(getName() + "  Apel: comert.anuleazaOferta( this, " + oferita + ", " + (-1) * cantitate + ", " + primaCeruta + ", " + aDouaCeruta + " );");
            try {
                anuleazaOferta(oferita, -cantitate, primaCeruta, aDouaCeruta);
            } catch (Exception ex) {
                ex.printStackTrace(System.out);
            }
        }
    }

    /*
     * public void vinResurseDinComert(int cantitate, Resursa res) {
     * synchronized (this.resurse) { switch (res) { case LEMN:
     * this.resurse.setLemn(this.resurse.getLemn() + cantitate); break; case
     * PIATRA: this.resurse.setPiatra(this.resurse.getPiatra() + cantitate);
     * break; case METAL: this.resurse.setMetal(this.resurse.getMetal() +
     * cantitate); break; } Main.debug(getName() + " ---- RESURSE DIN COMERT");
     * Main.debug(getName() + " Resurse primite din comert : " + cantitate + " "
     * + res); Main.debug(getName() + " Resurse cumulate : " + resurse.getLemn()
     * + " lemn, " + resurse.getPiatra() + " piatra, " + resurse.getMetal() + "
     * metal");
     *
     *
     * }
     * }
     */

    /*
     * private void iaResurseProducator() { int nrResPrimite; Resursa resCeruta;
     * Main.debug(getName() + " ---- RESURSE PRODUCATOR ----"); synchronized
     * (this.resurse) {
     *
     * if (resurse.getLemn() - target.getResurse().getLemn() < 0) { resCeruta =
     * Resursa.LEMN; } else if (resurse.getPiatra() -
     * target.getResurse().getPiatra() < 0) { resCeruta = Resursa.PIATRA; } else
     * { resCeruta = Resursa.METAL; } } Producator p =
     * this.res.getProducator(resCeruta); if (p != null) { nrResPrimite =
     * p.da_resurse(); synchronized (this.resurse) { switch (resCeruta) { case
     * LEMN: this.resurse.setLemn(this.resurse.getLemn() + nrResPrimite); break;
     * case PIATRA: this.resurse.setPiatra(this.resurse.getPiatra() +
     * nrResPrimite); break; case METAL:
     * this.resurse.setMetal(this.resurse.getMetal() + nrResPrimite); break; } }
     * res.adaugaProducator(p); Main.debug(getName() + " Resurse primite: " +
     * nrResPrimite + " " + resCeruta); Main.debug(getName() + " Resurse
     * cumulate : " + resurse.getLemn() + " lemn, " + resurse.getPiatra() + "
     * piatra, " + resurse.getMetal() + " metal");
     *
     *
     * } else { Main.debug(getName() + " Nu s-a gasit niciun producator liber
     * !!!"); } }
     */
    private void addResurse(Resursa res, int cantitate) {
        //System.out.println(getName() + " resurse din cladire " + res + " " + cantitate);
        synchronized (this.resurse) {
            switch (res) {
                case LEMN:
                    this.resurse.setLemn(this.resurse.getLemn() + cantitate);
                    break;
                case PIATRA:
                    this.resurse.setPiatra(this.resurse.getPiatra() + cantitate);
                    break;
                case METAL:
                    this.resurse.setMetal(this.resurse.getMetal() + cantitate);
                    break;
            }
        }
    }

    @Override
    public void onMessage(Message message) {
        String strMsg = null;
        try {
            strMsg = ((TextMessage) message).getText();
        } catch (JMSException ex) {
            ex.printStackTrace(System.out);
        }
        if (strMsg == null) {
            System.out.println("null message");
            return;
        }
        String[] split = strMsg.split(",");
        Resursa res = Resursa.valueOf(split[0]);
        int cant = Integer.valueOf(split[1]);
        addResurse(res, cant);
    }

    @Override
    public void onException(JMSException exception) {
        exception.printStackTrace(System.out);
    }

    private class CladireDeResurse extends Thread {

        private Resursa res;
        private int cant, timp;

        public CladireDeResurse(Resursa res, int cant, int timp) {
            this.res = res;
            this.cant = cant;
            this.timp = timp;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    Jucator.this.addResurse(res, cant);
                    synchronized (this) {
                        wait(timp);
                    }
                }
            } catch (InterruptedException ex) {
                System.out.println("End cladire de resurse");
            }
        }
    }
}
